/*
 * @(#)PRPAIN201310UV02Transformer.java
 * Date 2013-08-25
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.pix.transformer.hl7;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import org.hl7.v3.ActClassControlAct;
import org.hl7.v3.II;
import org.hl7.v3.MFMIMT700711UV01QueryAck;
import org.hl7.v3.PRPAIN201310UV02;
import org.hl7.v3.PRPAIN201310UV02MFMIMT700711UV01ControlActProcess;
import org.hl7.v3.PRPAIN201310UV02MFMIMT700711UV01RegistrationEvent;
import org.hl7.v3.PRPAIN201310UV02MFMIMT700711UV01Subject1;
import org.hl7.v3.PRPAIN201310UV02MFMIMT700711UV01Subject2;
import org.hl7.v3.PRPAMT201304UV02OtherIDs;
import org.hl7.v3.PRPAMT201304UV02Patient;
import org.hl7.v3.PRPAMT201304UV02Person;
import org.hl7.v3.PRPAMT201307UV02DataSource;
import org.hl7.v3.PRPAMT201307UV02QueryByParameter;
import org.hl7.v3.ParticipationTargetSubject;
import org.hl7.v3.XActMoodIntentEvent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lamppost.exception.ServiceActionException;

import us.hornerscorners.lamppost.hl7v3common.constant.AckResponse;
import us.hornerscorners.lamppost.hl7v3common.constant.AckSetting;
import us.hornerscorners.lamppost.hl7v3common.constant.ClassCode;
import us.hornerscorners.lamppost.hl7v3common.constant.ControlCode;
import us.hornerscorners.lamppost.hl7v3common.constant.HL7Constant;
import us.hornerscorners.lamppost.hl7v3common.constant.Interaction;
import us.hornerscorners.lamppost.hl7v3common.constant.MoodCode;
import us.hornerscorners.lamppost.hl7v3common.constant.ProcessingCode;
import us.hornerscorners.lamppost.hl7v3common.constant.ProcessingMood;
import us.hornerscorners.lamppost.hl7v3common.constant.StatusCode;
import us.hornerscorners.lamppost.hl7v3common.constant.TypeCode;

import us.hornerscorners.lamppost.hl7v3common.transformer.CDTransformer;
import us.hornerscorners.lamppost.hl7v3common.transformer.CSTransformer;
import us.hornerscorners.lamppost.hl7v3common.transformer.IITransformer;
import us.hornerscorners.lamppost.hl7v3common.transformer.MCCIMT000300UV01Transformer;
import us.hornerscorners.lamppost.hl7v3common.transformer.TSTransformer;

import us.hornerscorners.lamppost.hl7v3common.utils.HL7Utils;

import us.hornerscorners.lamppost.model.patient.Patient;

import us.hornerscorners.lamppost.pix.model.PatientQueryResponse;

import us.hornerscorners.lamppost.transformer.AbstractTransformer;


/**
 *
 * @author jim
 */
public class PRPAIN201310UV02Transformer
        extends AbstractTransformer<PatientQueryResponse, PRPAIN201310UV02> {

    /** Field description */
    private static final Logger logger =
        LoggerFactory.getLogger(PRPAIN201310UV02Transformer.class);

    /** Field description */
    @Inject
    private CDTransformer cdTransformer;

    /** Field description */
    @Inject
    private CSTransformer csTransformer;

    /** Field description */
    @Inject
    private IITransformer iiTransformer;

    /** Field description */
    @Inject
    private MCCIMT000300UV01Transformer mccimtTransformer;

    /** Field description */
    @Inject
    private PRPAMT201304UV02PatientTransformer patientTransformer;

    /** Field description */
    @Inject
    private TSTransformer tsTransformer;

    /**
     * Method description
     *
     *
     * @param queryParameters
     * @param prpaPatient
     */
    private void removeUnwantedIds(PRPAMT201307UV02QueryByParameter queryParameters,
                                   PRPAMT201304UV02Patient prpaPatient) {

        Set<String> roots = new HashSet<>();
        List<PRPAMT201307UV02DataSource> dataSources =
            queryParameters.getParameterList().getDataSource();

        for (PRPAMT201307UV02DataSource dataSource : dataSources) {

            for (II id : dataSource.getValue()) {

                logger.debug("Adding dataSource {}", id.getRoot());
                roots.add(id.getRoot());
            }
        }

        if (roots.isEmpty() == false) {

            List<II> idsRemove = new ArrayList<>();

            for (II id : prpaPatient.getId()) {

                if (roots.contains(id.getRoot()) == false) {

                    idsRemove.add(id);
                }
            }

            prpaPatient.getId().removeAll(idsRemove);

            // TODO investigate does dataSource influence otherIDs
            PRPAMT201304UV02Person prpaPerson = prpaPatient.getPatientPerson().getValue();

            // TODO investigate, remove entire otherIDs or just the ID in otherIDs
            List<PRPAMT201304UV02OtherIDs> othersRemove = new ArrayList<>();

            for (PRPAMT201304UV02OtherIDs otherId : prpaPerson.getAsOtherIDs()) {

                for (II id : otherId.getId()) {

                    if (roots.contains(id.getRoot()) == false) {

                        othersRemove.add(otherId);

                        break;
                    }
                }
            }

            prpaPerson.getAsOtherIDs().removeAll(othersRemove);
        }

    }

    /**
     * Method description
     *
     *
     *
     * @return
     */
    @Override
    public PRPAIN201310UV02 transform(PatientQueryResponse response)
            throws ServiceActionException {

        PRPAIN201310UV02 result = new PRPAIN201310UV02();

        result.setITSVersion(HL7Constant.ITSVersion.getCode());
        result.setId(this.iiTransformer.createRandomII());
        result.setCreationTime(this.tsTransformer.createNowTS());
        result.setInteractionId(
            this.iiTransformer.transform(Interaction.PRPA_IN201310UV02));
        result.setProcessingCode(this.csTransformer.transform(ProcessingCode.Production));
        result.setProcessingModeCode(
            this.csTransformer.transform(ProcessingMood.CurrentProcessing));
        result.setAcceptAckCode(this.csTransformer.transform(AckSetting.Never));

        // Receiver
        result.getReceiver().add(
            this.mccimtTransformer.transform(response.getPrpa309Request()));

        // Sender
        result.setSender(this.mccimtTransformer.transform(response.getSenderDevice()));

        // Acknowledgement
        result.getAcknowledgement().add(
            this.mccimtTransformer.transform(
                response.getPrpa309Request(), response.getAckResponse()));

        // ControlActProcess
        PRPAIN201310UV02MFMIMT700711UV01ControlActProcess controlAct =
            new PRPAIN201310UV02MFMIMT700711UV01ControlActProcess();

        controlAct.setClassCode(ActClassControlAct.CACT);
        controlAct.setMoodCode(XActMoodIntentEvent.EVN);
        controlAct.setCode(this.cdTransformer.transform(ControlCode.PRPA_TE201310UV02));

        // QueryAck
        MFMIMT700711UV01QueryAck queryAck = new MFMIMT700711UV01QueryAck();

        queryAck.setQueryId(response.getQueryParameters().getQueryId());
        queryAck.setQueryResponseCode(this.csTransformer.transform(AckResponse.OK));
        controlAct.setQueryAck(queryAck);

        // QueryByParameter
        controlAct
            .setQueryByParameter(
                HL7Utils.hl7Factory
                    .createPRPAIN201309UV02QUQIMT021001UV01ControlActProcessQueryByParameter(
                        response.getQueryParameters()));

        for (Patient patient : response.getPatients()) {

            PRPAIN201310UV02MFMIMT700711UV01Subject1 subject =
                new PRPAIN201310UV02MFMIMT700711UV01Subject1();

            subject.getTypeCode().add(TypeCode.Subject.getCode());

            PRPAIN201310UV02MFMIMT700711UV01RegistrationEvent regEvent =
                new PRPAIN201310UV02MFMIMT700711UV01RegistrationEvent();

            regEvent.getClassCode().add(ClassCode.Registration.getCode());
            regEvent.getMoodCode().add(MoodCode.EventPerformed.getCode());
            regEvent.getId().add(this.iiTransformer.createNullFlavorII());
            regEvent.setStatusCode(this.csTransformer.transform(StatusCode.Active));

            // TODO look into custodian
            // regEvent.setCustodian(null);

            PRPAIN201310UV02MFMIMT700711UV01Subject2 subject1 =
                new PRPAIN201310UV02MFMIMT700711UV01Subject2();

            subject1.setTypeCode(ParticipationTargetSubject.SBJ);

            // convert to HL7 v3
            PRPAMT201304UV02Patient prpaPatient =
                this.patientTransformer.transform(patient);

            // check dataSources, return only relevant ids
            removeUnwantedIds(response.getQueryParameters(), prpaPatient);

            // TODO investigate what happens if we return no IDs

            subject1.setPatient(prpaPatient);

            regEvent.setSubject1(subject1);

            subject.setRegistrationEvent(regEvent);
            controlAct.getSubject().add(subject);
        }

        result.setControlActProcess(controlAct);

        return result;
    }
}
